﻿using System;
using RayDen.Library.Components.Color.Types;
using RayDen.Library.Components.Surface;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.RayEngine.Corrector.Managers;

namespace RayEngine.Spectral.Entities.Brdfs
{
    public class MetalBrdf : BrdfBase
    {
        public override BrdfType Type
        {
            get { return BrdfType.Glossy | BrdfType.Specular; }
        }

        public override BrdfClass Class
        {
            get { return BrdfClass.GlossyMetal; }
        }

        public override void F(ShadePointInfo pt, out IColorType fs, BrdfType types = BrdfType.Diffuse)
        {
            throw new NotImplementedException();
        }

        public override void Sample_f(ShadePointInfo point, float u0, float u1, float u2, out Vector wi, out float radiancePdf, out float pdf, out BsdfEvent bsdfEvent)
        {
            wi = GlossyReflection(ref point.IncomingDirection, point.Exponent, ref point.ShadingNormal, u0, u1);
            wi = new ONB(ref point.ShadingNormal).ToWorld(ref wi);
            bsdfEvent = this.Type.TypeToEvent();
            if (Vector.Dot(ref wi, ref point.ShadingNormal) > 0f)
            {
                pdf = 1f;
                radiancePdf = 1.0f;
            }
            else
            {
                radiancePdf = 0f;
                pdf = 0f;
                bsdfEvent = BsdfEvent.Absorb;
            }
        }

        public override float Pdf(ref Vector wo, ref Vector wi)
        {
            throw new NotImplementedException();
        }

        public static Vector GlossyReflection(ref Vector wo, float exponent, ref Normal shadN, float u0, float u1)
        {
            var shadeN = shadN;
            float phi = 2f * MathLab.M_PI * u0;
            float cosTheta = (float)MathLab.Pow(1f - u1, exponent);
            float sinTheta = MathLab.Sqrt(1f - cosTheta * cosTheta);
            float x = (float)Math.Cos(phi) * sinTheta;
            float y = (float)Math.Sin(phi) * sinTheta;
            float z = cosTheta;

            Vector dir = -wo;
            float dp = Vector.Dot(ref shadeN, ref dir);
            Vector w = dir - (2f * dp) * shadeN.ToVec();

            Vector u;
            if (Math.Abs(shadeN.x) > .1f)
            {
                var a = new Vector(0f, 1f, 0f);
                u = Vector.Cross(ref a, ref w);
            }
            else
            {
                Vector a = new Vector(1f, 0f, 0f);
                u = Vector.Cross(ref a, ref w);
            }
            u = u.Normalize();
            Vector v = Vector.Cross(ref w, ref u);

            return x * u + y * v + z * w;
        }

    }
}
